The Basics About NS Basic
George Henne
NS BASIC Corporation
gh@hookup.net

THIS ARTICLE WAS FIRST PUBLISHED IN PDA DEVELOPERS (FORMERLY PIE DEVELOPERS)
MAGAZINE. COPYRIGHT (C) 1994 BY CREATIVE DIGITAL SYSTEMS. ALL RIGHTS RESERVED.
CONTACT CREATIVE DIGITAL SYSTEMS AT CDS@NETCOM.COM FOR MORE INFORMATION. 

Back when we first conceived of BASIC for the Newton, we wanted to create a
simple version of BASIC without a lot of frills. Once we got started, we
found that the NewtonScript architecture would allow us to make a
considerably more powerful implementation, without losing the advantages of
BASIC. 
	Without getting into the religious war about whether BASIC as a language
should even exist, you should remember that, like it or not, more people can
program in it than any other single language. Notwithstanding recurring
reports of its demise, BASIC is healthier than ever and constantly popping up
in new places. During the past 25 years I've been working with the language,
countless others have been proclaimed as the future of programming and then
fallen by the wayside.
	We felt that BASIC for the Newton should match the ease of use and
interactivity of BASIC on other platforms, and be a viable alternative for
many for whom the NTK is out of reach. It turns out that it can also be a
useful tool even for NTK experts.
	NewtonScript is itself an interpretive environment. When you build a
project under NTK, your code is translated into code blocks, made up of
tokenized symbols the Newton runtime can interpret. NS BASIC works in a
similar fashion - as you enter each line of BASIC code, it is translated into
NewtonScript and compiled into code blocks. No translation or compilation
takes place when you run the program: the Newton's runtime does most of the
work.
	The benefit of this implementation is that much of the extraordinarily
rich Newton environment becomes available to NS BASIC. 

Data Types
NS BASIC supports the same data types as NewtonScript: Integers, floating
points, strings, booleans and arrays work as you would expect in a BASIC.
There is an additional data type which is not usually found in BASIC called a
frame. Here is a frame sample:

* A={name: "Reg", type="Llama", age: 21} 
* PRINT A
{name:"Reg",type="Llama",age:21}

* PRINT A.name
Reg

	A frame is a collection of named fields, each of which can be any valid
data type, including an array or even another frame. Here's another example:

* zoo:=[A, {name: "Polly", type="Parrot", age: 3, 
	status: "Dead"}] 
* PRINT zoo[0]
{name:"Reg",type="Llama",age: 21}

* PRINT zoo[1].status && zoo[1].type
Dead Parrot

The data structure even accommodates multidimensional arrays. 

0010 DIM B[3]
0015 LET n=0
0020 FOR i=0 to 2
0030 	LET b[i]=array(4,0)
0040 	FOR j=0 to 3
0050		LET n=n+1
0060		LET B[i][j]=n
0070 	NEXT j
0080 NEXT i
0090 print b

* run
[[1,2,3,4],[5,6,7,8],[9,10,11,12]]

* print "B 1,3=" & b[1][3]
B 1,3=8

If you have the PDA Developers source code disk for this issue, you can cut
and paste these samples to your serial port while running NS BASIC to try
them.

Functions
Another advantage of harnessing the power of NewtonScript is that most of the
NewtonScript function library is available to NS BASIC programmers. The NS
BASIC Handbook tries to document just enough of the functions so that people
used to programming in other BASICs find most of the functions they are used
to. It keeps the manual to a manageable 150 pages, with lots of examples.
	At last count, however, there are over 600 NewtonScript functions To
document all of them would make the NS BASIC Handbook a discouragingly thick
book. You can, however, write a quick program in NS BASIC to get a complete
list of them:

* list
0010 LET x=elements(getglobals().functions) 
0020 for i=104 to 110 // just print a few of them 
0030	print x[i]
0040 next i

* run
Date
DateNTime
DeactivateTestAgent
debug
DebugMemoryStats
DebugRunUntilIdle
DeepClone

* print getglobals().functions.dateNtime.numargs 
1

	A few warnings are in order here. Some of these functions are useful.
Others are irrelevant when running in an NS BASIC environment, and still
others are downright dangerous and not even documented by the PIE Group. To
find out more about the functions, you might want to refer to Programming for
the Newton by McKeehan and Rhodes, NewtRTFM or the Newton Programmer's Guide
from Apple.

Accessing Soup Data
NS BASIC supports the creation and access of indexed files. To reflect their
flexibility and unusual nature, Newton files are called soups, but you can
think of them just like the files you use on other computers. 
	The Names and Notes applications that come with each Newton put their
information into soups. Here's a simple program that dumps all your Notes to
the screen:

0010 rem DumpNotes
0020 open ch,"notes",timestamp
0040 get ch,n
0050 if fstat=1 then end
0060 print datentime(n.timestamp)
0070 if length(n.data)=0 then goto 40
0080 for i=0 to length(n.data)-1
0090 	print n.data[i].text
0100 next i
0110 print " "
0120 GOTO 0040
0130 rem fin

* run
1/7/94 7:47 am
Buy new shaver

4/14/94 2:54 pm
life and Faith by Grossman
reorder lone tar

7/26/94 6:07 pm
call Richard
	
	To access a Newton soup you have to know its data structure. In most
cases, developers make it easy for us by putting their data into frames in a
straightforward manner. Because the fields of a frame are named, you can hack
out a lot of information about a program's soups. 
	Let's pick on True North's Gulliver to see show this is done. (By the
way, this is a good package - I'm not picking on it for any particular
reason):

* open ch,"airlines:TrueNorth"
* get ch,airRec
* print airRec

{ name: "ACES",code: "VX",phone: "800 846-2237",
corpNum: "",ftNum: "",forPop: 0, _uniqueID: 0,_modtime: 47669246} 

Now that we know the layout of each record, we can write a program to display
the file's contents:

0010 open ch,"airlines:TrueNorth"
0015 LET i=0
0020 get ch,airRec
0030 print airRec.name && airRec.phone
0040 LET i=i+1
0050 if i<6 then goto 20

* run
ACES 800 846-2237
Aer Lingus 800 223-6537
Aeroflot 800 995-5555
Aerolineas Argentinas 800 225-9920
Aeromexico 800 237-6639
Aeroperu 800 777-7717

	From this, you can see that it's pretty easy to use NS BASIC to write
programs that interact with the files (err...soups) that are on your Newton.
There's no reason why you can't also use the PUT command to modify the data
in those files if you exercise some common sense.

Accessing Other Applications
It's also easy to invoke parts of other applications as if they are part of
your NS BASIC program. To do this, you need know the proper names of the
functions and variables within the application you want to talk to. The best
way to get this information is to contact the developers of the target
application and get them to tell you.
	Failing that, you can always use Jason Harper's ViewFrame to figure it
out. I happen to like how Pelican Software's QuickFigure displays graphs, and
wanted to do it from NS BASIC. Poking around a bit with ViewFrame, I found
what needed to be set, and wrote the following: 

0010 LET a=[1,2,5,4,3,1,2,3,4,5]
0020 LET getroot().|qf_qchart:donv|.ddxdata=a 
0021 LET getroot().|qf_qchart:donv|.rows=5 
0022 LET getroot().|qf_qchart:donv|.cols=2 
0030 LET getroot().|qf_qchart:donv|.ddxfrom=
			'|basic:nsbasic| 
0040 LET x:=getroot().|qf_Qchart:donv|:open() 

	An interesting observation here is that NS BASIC supports external
routines written in NewtonScript. If you have need for a special function
that can only be done in NewtonScript, you can write it using NTK and then
call it from within an NS BASIC program.

Serial Communications
One of the toughest things to program on the Newton is communications. (This
is a bit ironic, since the Newton is marketed as a communications device.) To
keep within the NS BASIC philosophy, we needed an easy way to do serial
communications . BASIC already has perfectly good input and output statements
- INPUT and PRINT. By setting an environment variable, we can redirect these
to use the serial port instead of the screen. Here's the example we used for
Notes, modified to output your notes out the serial port. (This is actually
useful: when I came back from MacWorld I used it to upload all my notes to my
Mac.)

0010 rem dump all notes to serial port
0020 open ch,"notes",timestamp
0030 environ io="s0"
0040 get ch,n
0050 if fstat=1 then goto 0130
0060 print datentime(n.timestamp)
0070 if length(n.data)=0 then goto 40
0080 for i=0 to length(n.data)-1
0090 	print n.data[i].text
0100 next i
0110 print " "
0120 goto 0040
0130 rem fin
0140 environ io="screen"

The NS BASIC environ variable io points to the output location. (the only two
valid values for io are "screen" and "s0").
	There are a number of other environment variables that affect serial
communications:

s0.data - sets the baud rate, data bits, stop bits and parity. A wide range
of options are supported.
	
s0.connect - sets the message that's displayed down the serial line when
the serial line is opened. 

s0.unpend - sets the character NS BASIC waits for to end an INPUT
statement. Normally, this is a return key. 

s0.byteCount - sets the number of characters the Newton should accept before
ending an INPUT. Either byte-Count or unPend should be set, not both.

inputPrompt - is the character that is sent by the INPUT statement to
signify it is ready to receive input.

The Newton serial port, which is controlled by the XON/OFF protocol, is
capable of buffering a few hundred characters on input. If your program can't
process the data as fast as it is arriving, it will still probably be able to
handle the incoming data stream.

Windows and Buttons
During the development of NS BASIC, it soon became clear that a normal
character driven interface would not be very good on a Newton. We decided to
support windows and buttons. 
	The current version allows a fairly simple window construct, but with
enough options to make it useful. Here's a program that displays a simple
window:

0010 CLS
0020 let w1spec={}
0030 let w1spec.viewbounds=
			{left:15, right:220, top:15, bottom:60} 
0040 let w1spec.viewfont={family:'espy,face:3,size:14} 
0050 let w1spec.viewformat=
			6*vfRound+8*vfPen+vfFrameMatte+vfFillWhite 
0060 let w1spec.viewJustify=2
0070 WINDOW W1,W1SPEC
0080 SHOW W1
0100 WPRINT w1,"This is a window"
0110 WAIT 5*60 // display the window for 5 seconds 
0120 HIDE w1

In the above example, statements 30 to 60 are optional. In this case they
further specify the window's font and typeface, the border, the fill pat-tern
and text justification. If values are not specified, defaults are used.
	Any window can be turned into a button by adding a GOTO field into the
frame. The GOTO is to a line number in the NS BASIC program - tapping on the
window transfers execution to that line number. (Jeepers, Batman! Is this
event driven programming in BASIC?)
	Here's a sample program that set and clears a check mark in a box: 

0010 LET f={goto:1000}
0011 LET f.viewbounds=
			{left:100, right:110, top: 100, bottom: 110} 
0015 cls
0020 window w1,f
0030 show w1
0040 for i=1 to 3
0045 	PRINT i
0050 	wait
0070 next i
0080 stop
1000 rem toggle checkbox
1010 LET cbox=not cbox
1020 if cbox then wprint w1,chr(8730) else wprint w1,"" 

Note that lines 40 to 70 set up a loop with a WAIT statement in it. This puts
the Newt to sleep for a second, checks to see if there has been a tap, then
goes back to sleep. It's in a loop that runs just 3 times so that the program
stops. Since Newton applications are single threaded, NS BASIC must be in a
WAIT state to pick up a tap. If the BASIC program was running, the tap
couldn't be recognized.
	You can display multiple windows and checkboxes simultaneously. 

Functions
You can use almost any form of valid NewtonScript code as an NS BASIC
function. For compatibility, there are two forms that are basically
equivalent: 

DEF FN<functionName>(args)=<expression> 
FUNCTION <functionName>(args) expression 

<functionName> is a valid NS BASIC variable name and <expression> is a valid
expression or NewtonScript code. Variables within your NS BASIC program are
available within <expression>, even if they aren't an argument: preface them
with "U". To invoke an expression, use the form U:<functionName(args).
	Here's an example:

* list
0010 rem demonstrate use of functions
0020 def fns(starttime)=(ticks()-starttime)/60 
0030 function tot(b) begin local x:=0; 
			for i:=0 to length(b)-1 do x:=x+b[i]; x end
0040 LET iterations=1000
0050 let a=array(iterations, 25)
0060 GOSUB 0090 //sum using NS BASIC loop 
0070 GOSUB 0170 //sum using function
0080 stop
0085 rem
0090 rem sum using NS BASIC loop
0095 rem
0100 LET tm=ticks()
0110 LET x=0
0120 for i=0 to length(a)-1
0130 	LET x=x+a[i]
0140 next i
0150 print "Method 1:", u:fns(tm)
0160 return
0165 rem
0170 rem sum using function
0175 rem
0180 LET tm=ticks()
0190 LET x=u:tot(a)
0200 print "Method 2:", u:fns(tm)
0210 return

* run
Method 1: 110.383333333333
Method 2: 0.433333333333333
Stop at 0080
*

There are a number of ways this can be used to make NS BASIC programs do
really cool things. First, as the above example shows, it can make CPU bound
operations run quite a bit faster by bypassing the overhead and error
checking of NS BASIC. (Of course, if there's an error inside your
NewtonScript FUNCTION code, don't expect NS BASIC to be a lot of help.) You
get the most speed benefit from making functions out of heavily repeated
simple code. Operations that involve a call to the Newton's toolbox won't
benefit nearly as much.
	Second, there's virtually no limit to what you can put inside a function.
If the Newton environment lets you get away with it, NS BASIC isn't about to
get in your way. You can build some very sophisticated functions as a result.
	You can even save your functions into NS BASIC files. The function is
nothing more than some code, saved in a variable. There's no reason you can't
save it into a file. When a program starts, OPEN the file, GET the functions
from the file and assign them to variables in your program: 

GET ch,f
tot:=f.tot

You can now use u:tot(a) as a function in your program. In this way, you can
build up a entire libraries of functions. An even simpler way of doing the
same thing might be to assign the function to an ENVIRON variable and
retrieve it from there.

Conclusion
We've looked at some of the nonstandard things that can be done with NS BASIC
on a Newton. The language has some real advantages for those who don't have
NTK, don't want to learn a new language or don't have a Macintosh to run NTK.
It's good for straightforward applications, prototyping and as glue to hook
different applications together with. It's no substitute for NTK, but it has
its place. 

George Henne is the chief programmer on NS BASIC. He's been programming BASIC
for 25 years. While he's worked in over twenty different languages over the
years (his personal favorite is SNOBOL-IV), somehow BASIC keeps on coming
back into his life. 
